﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SQLite;
using System.Windows;

namespace KeyboardImageViewer
{
    public class ViewerDatabaseConfig
    {
        private SQLiteConnection connection;

        private Dictionary<string, WindowSize> cachedWindowSizes;

        private DirectorySet sourceDirectories;

        private bool updatesEnabled;

        private bool trackDuplicates;
        private bool trackImageDuplicates;

        private bool searchFileName;
        private bool searchTags;

        private int startingListId;

        private string expandedDirectories;

        private bool deleteDuplicatesOnImport;

        private FullscreenStart fullscreenStart;
        private bool overlayInfobar;

        private List<KeyValuePair<string, string>> tagDependencies;

        public ViewerDatabaseConfig(SQLiteConnection connection)
        {
            this.connection = connection;

            this.cachedWindowSizes = new Dictionary<string, WindowSize>();

            this.updatesEnabled = this.GetConfigBool("UpdatesEnabled");

            this.trackDuplicates = this.GetConfigBool("TrackDuplicates");
            this.trackImageDuplicates = this.GetConfigBool("TrackImageDuplicates");

            this.searchFileName = this.GetConfigBool("SearchFileName");
            this.searchTags = this.GetConfigBool("SearchTags");

            this.startingListId = this.GetConfigInt("StartingListId");

            this.expandedDirectories = this.GetConfigString("ExpandedDirectories");

            this.deleteDuplicatesOnImport = this.GetConfigBool("DeleteDuplicatesOnImport");

            this.fullscreenStart = (FullscreenStart)this.GetConfigInt("FullscreenStartSetting");
            this.overlayInfobar = this.GetConfigBool("OverlayInfobar");
        }

        public ViewerDatabaseConfig(SQLiteConnection connection, bool caching)
        {
            this.connection = connection;
        }

        public DirectorySet SourceDirectories
        {
            get
            {
                if (this.sourceDirectories == null)
                {
                    this.sourceDirectories = new DirectorySet();

                    SQLiteCommand getBaseDirectories = new SQLiteCommand("SELECT id, path, missing FROM baseDirectories", this.connection);
                    using (SQLiteDataReader reader = getBaseDirectories.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            BaseDirectory baseDirectory = new BaseDirectory { Path = reader.GetString("path") };

                            baseDirectory.Missing = reader.GetBoolean("missing");

                            SQLiteCommand getExcludedDirectories = new SQLiteCommand("SELECT path FROM excludedDirectories WHERE baseId = " + reader.GetInt32("id"), this.connection);
                            using (SQLiteDataReader excludedReader = getExcludedDirectories.ExecuteReader())
                            {
                                while (excludedReader.Read())
                                {
                                    baseDirectory.Exclusions.Add(excludedReader.GetString("path"));
                                }
                            }

                            this.sourceDirectories.BaseDirectories.Add(baseDirectory);
                        }
                    }
                }

                return this.sourceDirectories;
            }

            set
            {
                ViewerDatabaseConfig.UpdateSourceDirectories(value, this.connection);

                this.sourceDirectories = value;
            }
        }

        #region Config Settings
        public bool UpdatesEnabled
        {
            get
            {
                return this.updatesEnabled;
            }

            set
            {
                if (value != this.updatesEnabled)
                {
                    this.updatesEnabled = value;
                    this.SetConfigValue("UpdatesEnabled", value);
                }
            }
        }

        public bool TrackDuplicates
        {
            get
            {
                return this.trackDuplicates;
            }

            set
            {
                if (value != this.trackDuplicates)
                {
                    this.trackDuplicates = value;
                    this.SetConfigValue("TrackDuplicates", value);
                }
            }
        }

        public bool TrackImageDuplicates
        {
            get
            {
                return this.trackImageDuplicates;
            }

            set
            {
                if (value != this.trackImageDuplicates)
                {
                    this.trackImageDuplicates = value;
                    this.SetConfigValue("TrackImageDuplicates", value);
                }
            }
        }

        public bool SearchFileName
        {
            get
            {
                return this.searchFileName;
            }

            set
            {
                if (value != this.searchFileName)
                {
                    this.searchFileName = value;
                    this.SetConfigValue("SearchFileName", value);
                }
            }
        }

        public bool SearchTags
        {
            get
            {
                return this.searchTags;
            }

            set
            {
                if (value != this.searchTags)
                {
                    this.searchTags = value;
                    this.SetConfigValue("SearchTags", value);
                }
            }
        }

        public int StartingListId
        {
            get
            {
                return this.startingListId;
            }

            set
            {
                if (value != this.startingListId)
                {
                    this.startingListId = value;
                    this.SetConfigValue("StartingListId", value);
                }
            }
        }

        public List<string> ExpandedDirectories
        {
            get
            {
                string[] parts = this.expandedDirectories.Split(new char[] {'|'});
                return new List<string>(parts);
            }

            set
            {
                StringBuilder result = new StringBuilder();

                for (int i = 0; i < value.Count; i++)
                {
                    result.Append(value[i]);

                    if (i != value.Count - 1)
                    {
                        result.Append("|");
                    }
                }

                this.expandedDirectories = result.ToString();
                this.SetConfigValue("ExpandedDirectories", this.expandedDirectories);
            }
        }

        public List<string> ImportRules
        {
            get
            {
                var result = new List<string>();

                using (SQLiteCommand command = new SQLiteCommand("SELECT rule FROM importRules", this.connection))
                {
                    using (SQLiteDataReader reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            result.Add(reader.GetString("rule"));
                        }
                    }
                }

                return result;
            }

            set
            {
                using (SQLiteTransaction transaction = this.connection.BeginTransaction())
                {
                    Database.ExecuteNonQuery("DELETE FROM importRules", this.connection);

                    SQLiteCommand insertCommand = new SQLiteCommand(this.connection);

                    try
                    {
                        var ruleParameter = new SQLiteParameter();

                        insertCommand.CommandText = "INSERT INTO importRules (rule) VALUES (?)";
                        insertCommand.Parameters.Add(ruleParameter);

                        foreach (string rule in value)
                        {
                            ruleParameter.Value = rule;
                            insertCommand.ExecuteNonQuery();
                        }

                        transaction.Commit();
                    }
                    finally
                    {
                        if (insertCommand != null)
                        {
                            insertCommand.Dispose();
                        }
                    }
                }
            }
        }

        public bool DeleteDuplicatesOnImport
        {
            get
            {
                return this.deleteDuplicatesOnImport;
            }

            set
            {
                if (value != this.deleteDuplicatesOnImport)
                {
                    this.deleteDuplicatesOnImport = value;
                    this.SetConfigValue("DeleteDuplicatesOnImport", value);
                }
            }
        }

        public List<KeyValuePair<string, string>> TagDependencies
        {
            get
            {
                if (this.tagDependencies == null)
                {
                    this.tagDependencies = new List<KeyValuePair<string, string>>();

                    SQLiteCommand getDependencies = new SQLiteCommand("SELECT tag, dependentTag FROM tagDependencies", this.connection);
                    using (SQLiteDataReader reader = getDependencies.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            this.tagDependencies.Add(new KeyValuePair<string, string>(reader.GetString("tag"), reader.GetString("dependentTag")));
                        }
                    }
                }

                return this.tagDependencies;
            }

            set
            {
                this.tagDependencies = value;

                using (SQLiteTransaction transaction = this.connection.BeginTransaction())
                {
                    SQLiteCommand clearDependencies = new SQLiteCommand("DELETE FROM tagDependencies", this.connection);
                    clearDependencies.ExecuteNonQuery();

                    var tagParameter = new SQLiteParameter();
                    var depTagParameter = new SQLiteParameter();

                    SQLiteCommand insertDepTag = new SQLiteCommand(this.connection);
                    insertDepTag.CommandText = "INSERT INTO tagDependencies (tag, dependentTag) VALUES (?, ?)";
                    insertDepTag.Parameters.Add(tagParameter);
                    insertDepTag.Parameters.Add(depTagParameter);

                    foreach (KeyValuePair<string, string> dependency in this.tagDependencies)
                    {
                        tagParameter.Value = dependency.Key;
                        depTagParameter.Value = dependency.Value;

                        insertDepTag.ExecuteNonQuery();
                    }

                    transaction.Commit();
                }
            }
        }

        public FullscreenStart FullscreenStartSetting
        {
            get
            {
                return this.fullscreenStart;
            }

            set
            {
                if (value != this.fullscreenStart)
                {
                    this.fullscreenStart = value;
                    this.SetConfigValue("FullscreenStartSetting", (int)value);
                }
            }
        }

        public bool OverlayInfobar
        {
            get
            {
                return this.overlayInfobar;
            }

            set
            {
                if (value != this.overlayInfobar)
                {
                    this.overlayInfobar = value;
                    this.SetConfigValue("OverlayInfobar", value);
                }
            }
        }

        public bool FullscreenStart
        {
            get
            {
                return this.GetConfigBool("FullscreenStart");
            }

            set
            {
                this.SetConfigValue("FullscreenStart", value);
            }
        }

        #endregion

        public void ApplyWindowSize(string windowName, Window window)
        {
            if (!this.cachedWindowSizes.ContainsKey(windowName))
            {
                WindowSize newWindowSize = new WindowSize { Width = this.GetConfigDouble(windowName + "Width"), Height = this.GetConfigDouble(windowName + "Height") };
                this.cachedWindowSizes.Add(windowName, newWindowSize);
            }

            if (this.cachedWindowSizes[windowName].Width > 0)
            {
                window.Width = this.cachedWindowSizes[windowName].Width;
            }

            if (this.cachedWindowSizes[windowName].Height > 0)
            {
                window.Height = this.cachedWindowSizes[windowName].Height;
            }
        }

        public void SaveWindowSize(string windowName, Window window)
        {
            double width = window.ActualWidth;
            double height = window.ActualHeight;

            if (this.cachedWindowSizes.ContainsKey(windowName))
            {
                if (this.cachedWindowSizes[windowName].Width == width && this.cachedWindowSizes[windowName].Height == height)
                {
                    return;
                }

                this.cachedWindowSizes.Remove(windowName);
            }

            this.SetConfigValue(windowName + "Width", width);
            this.SetConfigValue(windowName + "Height", height);

            this.cachedWindowSizes.Add(windowName, new WindowSize { Width = width, Height = height });
        }

        public static void UpdateSourceDirectories(DirectorySet sourceDirectories, SQLiteConnection connection)
        {
            using (SQLiteTransaction transaction = connection.BeginTransaction())
            {
                Database.ExecuteNonQuery("DELETE FROM baseDirectories", connection);
                Database.ExecuteNonQuery("DELETE FROM excludedDirectories", connection);

                SQLiteCommand insertBaseDirectory = new SQLiteCommand("INSERT INTO baseDirectories (id, path, missing) VALUES (?, ?, ?)", connection);
                SQLiteParameter baseIdParam = new SQLiteParameter();
                SQLiteParameter basePathParam = new SQLiteParameter();
                SQLiteParameter baseMissingParam = new SQLiteParameter();
                insertBaseDirectory.Parameters.Add(baseIdParam);
                insertBaseDirectory.Parameters.Add(basePathParam);
                insertBaseDirectory.Parameters.Add(baseMissingParam);

                SQLiteCommand insertExcludedDirectory = new SQLiteCommand("INSERT INTO excludedDirectories (baseId, path) VALUES (?, ?)", connection);
                SQLiteParameter excludedIdParam = new SQLiteParameter();
                SQLiteParameter excludedPathParam = new SQLiteParameter();
                insertExcludedDirectory.Parameters.Add(excludedIdParam);
                insertExcludedDirectory.Parameters.Add(excludedPathParam);

                int baseDirId = 1;

                foreach (BaseDirectory baseDirectory in sourceDirectories.BaseDirectories)
                {
                    baseIdParam.Value = baseDirId;
                    basePathParam.Value = baseDirectory.Path;
                    baseMissingParam.Value = baseDirectory.Missing;

                    insertBaseDirectory.ExecuteNonQuery();

                    foreach (string excludedDirectory in baseDirectory.Exclusions)
                    {
                        excludedIdParam.Value = baseDirId;
                        excludedPathParam.Value = excludedDirectory;

                        insertExcludedDirectory.ExecuteNonQuery();
                    }

                    baseDirId++;
                }

                transaction.Commit();
            }
        }

        public bool GetConfigBool(string configName)
        {
            return DatabaseConfigAccess.GetConfigBool(configName, this.connection);
        }

        public double GetConfigDouble(string configName)
        {
            return DatabaseConfigAccess.GetConfigDouble(configName, this.connection);
        }

        public int GetConfigInt(string configName)
        {
            return DatabaseConfigAccess.GetConfigInt(configName, this.connection);
        }

        public string GetConfigString(string configName)
        {
            return DatabaseConfigAccess.GetConfigString(configName, this.connection);
        }

        public void SetConfigValue(string configName, bool value)
        {
            DatabaseConfigAccess.SetConfigValue(configName, value, this.connection);
        }

        public void SetConfigValue(string configName, double value)
        {
            DatabaseConfigAccess.SetConfigValue(configName, value, this.connection);
        }

        public void SetConfigValue(string configName, int value)
        {
            DatabaseConfigAccess.SetConfigValue(configName, value, this.connection);
        }

        public void SetConfigValue(string configName, string configValue)
        {
            DatabaseConfigAccess.SetConfigValue(configName, configValue, this.connection);
        }
    }
}
